package timing.ukulele.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.nimbusds.jose.JWSObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import timing.ukulele.gateway.SecurityConstants;
import timing.ukulele.gateway.config.properties.SecurityProperties;
import timing.ukulele.gateway.service.SecurityUserDetailService;
import timing.ukulele.gateway.support.RedisOperator;
import timing.ukulele.redisson.cache.CacheManager;

import java.net.URLEncoder;
import java.util.Map;

import static timing.ukulele.share.Constant.*;

/**
 * 安全拦截全局过滤器
 * 在 ResourceServerManager#check 鉴权之后执行
 */
@Component
@Slf4j
public class SecurityGlobalFilter implements GlobalFilter, Ordered {
    private final SecurityUserDetailService userDetailService;
    private final SecurityProperties securityProperties;
    private final RedisOperator<String> redisOperator;

    public SecurityGlobalFilter(SecurityUserDetailService userDetailService,
                                SecurityProperties securityProperties,
                                RedisOperator<String> redisOperator) {
        this.userDetailService = userDetailService;
        this.securityProperties = securityProperties;
        this.redisOperator = redisOperator;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        // 非JWT放行不做后续解析处理
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(SecurityConstants.AUTHORIZATION_KEY);
        if (!StringUtils.hasLength(token) || !StringUtils.startsWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) {
            return chain.filter(exchange);
        }
        token = StringUtils.replace(token, SecurityConstants.JWT_PREFIX, Strings.EMPTY);
        ServerHttpRequest.Builder mutate = request.mutate();
        boolean success = true;
        try {
            JWSObject parse = JWSObject.parse(token);
            Map<String, Object> payloadMap = parse.getPayload().toJSONObject();
            // 根据缓存取用户信息
            String cacheUser = redisOperator.get(TOKEN_CACHE_PREFIX + payloadMap.get("sub"));
            log.info("从缓存中获取用户信息:{}", cacheUser);
            String username = null;
            String userId = null;
            String name = null;
            if (StringUtils.hasText(cacheUser)) {
                JSONObject userInfo = JSON.parseObject(cacheUser);
                username = userInfo.getString(TOKEN_USER_INFO_USERNAME);
                userId = userInfo.getString(TOKEN_USER_INFO_ID);
                name = userInfo.getString(TOKEN_USER_INFO_NAME);
                if (StringUtils.hasText(username)) {
                    String tenant = headers.getFirst(HEADER_TENANT);
                    if (securityProperties.getResourceBasedAccessControlEnabled() == null || securityProperties.getResourceBasedAccessControlEnabled()) {
                        success = userDetailService.checkUserRoleResource(request.getURI().getPath(), request.getMethod().name(), username, tenant);
                    }
                }
            }
            if (!success) {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.FORBIDDEN);
                return response.setComplete();
            }
            mutate.header(HEADER_USER, username);
            mutate.header(HEADER_USER_ID, userId);
            mutate.header(HEADER_USER_NAME, URLEncoder.encode(name, "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
            mutate.header(HEADER_USER, "");
            mutate.header(HEADER_USER_ID, "");
            mutate.header(HEADER_USER_NAME, "");
            log.error(String.format("向请求头中添加用户信息失败:%s", e.getMessage()));
        }
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
